using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace LucyXNA
{
   /// <summary>
   /// This is the main type for your game
   /// </summary>
   public class Game1 : Microsoft.Xna.Framework.Game
   {
      GraphicsDeviceManager graphics;
      List<VertexPositionColor> vertices_ = new List<VertexPositionColor>();
      Effect effect_;
      float angle_ = 0.0f;
      Matrix view_;
      Matrix projection_;

      public Game1()
      {
         graphics = new GraphicsDeviceManager(this);
         Content.RootDirectory = "Content";
      }

      /// <summary>
      /// Allows the game to perform any initialization it needs to before starting to run.
      /// This is where it can query for any required services and load any non-graphic
      /// related content.  Calling base.Initialize will enumerate through any components
      /// and initialize them as well.
      /// </summary>
      protected override void Initialize()
      {
         base.Initialize();
      }

      /// <summary>
      /// LoadContent will be called once per game and is the place to load
      /// all of your content.
      /// </summary>
      protected override void LoadContent()
      {
         // TODO: use this.Content to load your game content here
         Window.Title = "LucyXNA - icosahedron";

         graphics.PreferredBackBufferHeight = 500;
         graphics.PreferredBackBufferWidth = 500;
         effect_ = Content.Load<Effect>("effects");
         view_ = Matrix.CreateLookAt(1.6f * Vector3.UnitZ, Vector3.Zero, Vector3.UnitY);
         projection_ = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 50.0f);

         Geometry.Icosahedron.clockwise = true;
         Geometry.Icosahedron icos = new Geometry.Icosahedron(1.0f);

         List<Geometry.Triangle> triangles = icos.triangles.ToList();                  // 20 triangles
         //triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 80
         //triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 320
         //triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 1280
         //triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 5120
         //triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 20480
         //triangles = triangles.SelectMany(triangle => triangle.into4).ToList();        // 81920
         triangles.ForEach(triangle => triangle.vertices.ToList().ForEach(vertex =>
         {
            // Project the point out to our sphere of radius 4.0
            float scale = 1.0f / (float)Math.Sqrt(vertex.x * vertex.x + vertex.y * vertex.y + vertex.z * vertex.z);
            vertex = vertex.Add(0.0f, 0.0f, 0.0f).Mul(scale);

            // Vertices vary from -1.0 to +1.0, we want colours to vary from 0 to 1.0
            Geometry.Point3D colors = vertex.Add(1.0f, 1.0f, 1.0f).Mul(0.5f);
            //GL.Color3(colors.x, colors.y, colors.z);

            float radius = 0.5f;
            vertex = vertex.Mul(radius);
            vertices_.Add(new VertexPositionColor(new Vector3(vertex.x, vertex.y, vertex.z), new Color(colors.x, colors.y, colors.z)));
         }));
      }

      /// <summary>
      /// UnloadContent will be called once per game and is the place to unload
      /// all content.
      /// </summary>
      protected override void UnloadContent()
      {
         // TODO: Unload any non ContentManager content here
      }

      /// <summary>
      /// Allows the game to run logic such as updating the world,
      /// checking for collisions, gathering input, and playing audio.
      /// </summary>
      /// <param name="gameTime">Provides a snapshot of timing values.</param>
      protected override void Update(GameTime gameTime)
      {
         // Allows the game to exit
         if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();

         // TODO: Add your update logic here
         angle_ += 0.005f;

         base.Update(gameTime);
      }

      /// <summary>
      /// This is called when the game should draw itself.
      /// </summary>
      /// <param name="gameTime">Provides a snapshot of timing values.</param>
      protected override void Draw(GameTime gameTime)
      {
         GraphicsDevice.Clear(Color.CornflowerBlue);

         effect_.CurrentTechnique = effect_.Techniques["ColoredNoShading"];
         effect_.Parameters["xView"].SetValue(view_);
         effect_.Parameters["xProjection"].SetValue(projection_);

         Vector3 axisWobbly = new Vector3(3 * angle_, angle_, 2 * angle_);
         axisWobbly.Normalize();

         effect_.Parameters["xWorld"].SetValue(Matrix.CreateFromAxisAngle(axisWobbly, 3 * angle_));

         // TODO: Add your drawing code here
         foreach (EffectPass pass in effect_.CurrentTechnique.Passes)
         {
            pass.Apply();
            graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>
               (PrimitiveType.TriangleList, vertices_.ToArray(), 0, vertices_.Count / 3);
         }
         base.Draw(gameTime);
      }
   }
}
